package com.vf.cloud.pass.common.factory;

import java.util.Calendar;
import java.util.Date;
import java.util.Iterator;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;

import com.jfinal.kit.StrKit;
import com.vf.cloud.pass.common.domain.biz.Project;
import com.vf.cloud.pass.common.domain.biz.Scene;
import com.vf.cloud.pass.common.domain.biz.Terminal;
import com.vf.cloud.pass.common.domain.biz.TerminalGpu;
import com.vf.cloud.pass.common.domain.sys.User;
import com.vf.cloud.pass.common.util.DateUtil;
import com.vf.cloud.pass.common.util.R;
import com.vf.cloud.pass.common.vo.ProjectRendering;

import lombok.extern.slf4j.Slf4j;

@Slf4j
public class GPUFactory {

	private static volatile GPUFactory INSYANCE;

	public static GPUFactory getInstance() {
		if (null == INSYANCE) {
			synchronized (GPUFactory.class) {
				if (null == INSYANCE) {
					INSYANCE = new GPUFactory();
				}
			}
		}
		return INSYANCE;
	}

	private ConcurrentHashMap<String, ProjectRendering> GPU_USED_RESOURCE = new ConcurrentHashMap<String, ProjectRendering>();
	private ConcurrentHashMap<String, String> GPU_FREE_RESOURCE = new ConcurrentHashMap<String, String>();
	private ConcurrentHashMap<String, ProjectRendering> TIME_OUT = new ConcurrentHashMap<String, ProjectRendering>();
	
	
	public List<ProjectRendering> getList(User user){
		List<ProjectRendering> list = new LinkedList<ProjectRendering>();
		Iterator<String> it = TIME_OUT.keySet().iterator();
		while (it.hasNext()) {
			String gpuindex = it.next();
			ProjectRendering projectRendering = TIME_OUT.get(gpuindex);
			if(projectRendering!=null ) {
				if(StrKit.isBlank(user.getOrgId())) {
					list.add(projectRendering);
				}else {
					if(StrKit.equals(projectRendering.getOrgId(),user.getOrgId())) {
						list.add(projectRendering);
					}
				}
			}
		}
		Iterator<String> usedIt = GPU_USED_RESOURCE.keySet().iterator();
		while (usedIt.hasNext()) {
			String gpuindex = usedIt.next();
			ProjectRendering projectRendering = GPU_USED_RESOURCE.get(gpuindex);
			if(projectRendering!=null ) {
				if(StrKit.isBlank(user.getOrgId())) {
					list.add(projectRendering);
				}else {
					if(StrKit.equals(projectRendering.getOrgId(),user.getOrgId())) {
						list.add(projectRendering);
					}
				}
			}
		}
		return list;
	}
	

	private List<String> removeKeys = new LinkedList<String>();

	public void check() {
		if (TIME_OUT.size() <= 0) {
			return;
		}
		log.info(String.format("GPU_WAIT_RESOURCE :%s", TIME_OUT.size()));
		Iterator<String> it = TIME_OUT.keySet().iterator();
		while (it.hasNext()) {
			String gpuindex = it.next();
			if (!GPU_USED_RESOURCE.containsKey(gpuindex)) {
				ProjectRendering projectRendering = TIME_OUT.get(gpuindex);
				if (projectRendering != null) {
					if (new Date(System.currentTimeMillis()).getTime() > projectRendering.getTimeout().getTime()) {
						TIME_OUT.remove(gpuindex);
						GPU_FREE_RESOURCE.put(gpuindex, "");
						log.info(String.format("GPU is Timeout,Kill:%s", gpuindex));
					}
				}
			} else {
				ProjectRendering projectRendering = TIME_OUT.get(gpuindex);
				if (projectRendering != null) {
					projectRendering.setConnected(true);	
					projectRendering.setConnectedTime(DateUtil.getLocalDateTime());
					GPU_USED_RESOURCE.put(gpuindex, TIME_OUT.get(gpuindex));
				}
				removeKeys.add(gpuindex);
			}
		}
		for (String gpuindex : removeKeys) {
			if (TIME_OUT.containsKey(gpuindex)) {
				TIME_OUT.remove(gpuindex);
				log.info(String.format("GPU remove Timeout :%s", gpuindex));
			}
		}
		removeKeys.clear();
	}

	public void removeTimeout(String key) {
		if (!GPU_USED_RESOURCE.containsKey(key)) {
			if (GPU_FREE_RESOURCE.containsKey(key)) {
				GPU_FREE_RESOURCE.put(key, "");
			}

			if (TIME_OUT.containsKey(key)) {
				TIME_OUT.remove(key);
			}
		} else {
			if (GPU_FREE_RESOURCE.containsKey(key)) {
				GPU_FREE_RESOURCE.remove(key);
			}

			if (TIME_OUT.containsKey(key)) {
				TIME_OUT.remove(key);
			}
		}

	}

	private Date getLaterTime() {
		Calendar cal = Calendar.getInstance();
		cal.add(Calendar.MINUTE, 1);
		return cal.getTime();
	}

	public void use(String gpu, String src) {
		if (!StrKit.isBlank(src)) {
			ProjectRendering projectRendering = new ProjectRendering();
			projectRendering.setConnected(false);
			projectRendering.setOrder(src);
			projectRendering.setKey(gpu);
			projectRendering.setStartTime(DateUtil.getLocalDateTime());
			projectRendering.setTimeout(getLaterTime());
			projectRendering.setConnectedTime(DateUtil.getLocalDateTime());
			
			Project project=Project.dao.findById(src);
			if(project!=null) {
				projectRendering.setProjectName(project.getName());
				projectRendering.setOrgId(project.getOrgId());
				Scene scene=Scene.dao.findById(project.getSceneId());
				if(scene!=null) {
					projectRendering.setSceneName(scene.getName());
				}
				
				User user=User.dao.findById(project.getOrgId());
				if(user!=null) {
					projectRendering.setOrgName(user.getOrgName());
				}
				
				String[] renderingKeyArr=gpu.split("\\|");
				Terminal terminal=Terminal.dao.findById(renderingKeyArr[0]);
				if(terminal!=null) {
					projectRendering.setTerminaName(terminal.getName());
				}
				
			}
			
			GPU_USED_RESOURCE.put(gpu, projectRendering);
		}

		if (GPU_FREE_RESOURCE.containsKey(gpu)) {
			GPU_FREE_RESOURCE.remove(gpu);
		}

		if (TIME_OUT.containsKey(gpu)) {
			TIME_OUT.remove(gpu);
		}

	}

	public R<String> apply(String order, Map<String, String> rendering,Project project) {
		if (GPU_FREE_RESOURCE.size() <= 0) {
			return R.failed("渲染资源已达上线.");
		}
		String renderingKey = "";
		Iterator<String> it = GPU_FREE_RESOURCE.keySet().iterator();
		while (it.hasNext()) {
			String key = it.next();
			if (rendering.containsKey(key.substring(0, key.indexOf("|")).trim())) {
				renderingKey = key;
			}
			break;
		}
		if (StrKit.isBlank(renderingKey)) {
			return R.failed("渲染资源已达上线.");
		}

		ProjectRendering projectRendering = new ProjectRendering();
		projectRendering.setConnected(false);
		projectRendering.setOrder(order);
		projectRendering.setKey(renderingKey);
		projectRendering.setStartTime(DateUtil.getLocalDateTime());
		projectRendering.setTimeout(getLaterTime());
		projectRendering.setProjectName(project.getName());
		projectRendering.setOrgId(project.getOrgId());
		
		Scene scene=Scene.dao.findById(project.getSceneId());
		if(scene!=null) {
			projectRendering.setSceneName(scene.getName());
		}
		
		User user=User.dao.findById(project.getOrgId());
		if(user!=null) {
			projectRendering.setOrgName(user.getOrgName());
		}
		
		String[] renderingKeyArr=renderingKey.split("\\|");
		Terminal terminal=Terminal.dao.findById(renderingKeyArr[0]);
		if(terminal!=null) {
			projectRendering.setTerminaName(terminal.getName());
		}
		
		GPU_FREE_RESOURCE.remove(renderingKey);
		TIME_OUT.put(renderingKey, projectRendering);
		return R.ok(renderingKey);
	}

	/**
	 * 初始化节点资源
	 * 
	 * @param terminalId
	 */
	public void init(String terminalId) {
		List<TerminalGpu> list = TerminalGpu.dao
				.find("select * from " + TerminalGpu.TABLE_NAME + " where terminal_id=?", terminalId);
		for (TerminalGpu g : list) {
			for (int i = 0; i < g.getGpuLimit(); i++) {
				if (!GPU_USED_RESOURCE.containsKey(String.format("%s|%s|%s", terminalId, g.getGpuIndex(), i))) {
					GPU_FREE_RESOURCE.put(String.format("%s|%s|%s", terminalId, g.getGpuIndex(), i), "");
				}
			}
		}
	}

	/**
	 * 失去连接清除节点资源
	 * 
	 * @param terminalId
	 */
	public void disconnected(String terminalId) {
		List<TerminalGpu> list = TerminalGpu.dao
				.find("select * from " + TerminalGpu.TABLE_NAME + " where terminal_id=?", terminalId);
		for (TerminalGpu g : list) {
			for (int i = 0; i < g.getGpuLimit(); i++) {
				GPU_USED_RESOURCE.remove(String.format("%s|%s|%s", terminalId, g.getGpuIndex(), i));
				GPU_FREE_RESOURCE.remove(String.format("%s|%s|%s", terminalId, g.getGpuIndex(), i));
			}
		}

	}

	public void onStreamerDisconnected(String key) {
		if (GPU_USED_RESOURCE.containsKey(key)) {
			GPU_USED_RESOURCE.remove(key);
		}

		if (TIME_OUT.containsKey(key)) {
			TIME_OUT.remove(key);
		}
		GPU_FREE_RESOURCE.put(key, "");
	}

	public void onStreamerConnected(String key) {
		if (GPU_FREE_RESOURCE.containsKey(key)) {
			GPU_FREE_RESOURCE.remove(key);
		}
		
		ProjectRendering projectRendering = TIME_OUT.get(key);
		if(projectRendering!=null) {
			if (!GPU_USED_RESOURCE.containsKey(key)) {
				projectRendering.setConnected(true);	
				projectRendering.setConnectedTime(DateUtil.getLocalDateTime());
				GPU_USED_RESOURCE.put(key, projectRendering);
			}
		}

		if (TIME_OUT.containsKey(key)) {
			TIME_OUT.remove(key);
		}

		if (!GPU_USED_RESOURCE.containsKey(key)) {
			GPU_USED_RESOURCE.put(key, null);
		}
	}

}
